/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * TrainingBook
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Track.hpp>
#include <libxml++/libxml++.h>
#include <iostream>

///@brief Default constructor
Track::Track()
{

}
		
///@brief Copy constructor
///@param t Track object to copy
Track::Track(const Track& t)
{
	segments = t.segments;
}

///@brief Destructo
Track::~Track()
{
	
}

///@brief Read track from Gpx file
///@param path Path to the GPX file to read
///@return true if succes
bool Track::ReadGPX(std::string path)
{
	try
	{
		xmlpp::DomParser parser;
		//parser.set_validate();
		parser.set_substitute_entities();	//We just want the text to be resolved/unescaped automatically
		parser.parse_file(path);
		if(parser)
		{
			//Walk the tree
			const xmlpp::Node* pNode = parser.get_document()->get_root_node();
			const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(pNode);
			const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(pNode);
			const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(pNode);

			Glib::ustring nodename = pNode->get_name();
			if(nodename == "gpx")	//root node corresponds to a gpx file
			{
				xmlpp::Node::NodeList list = pNode->get_children();
				for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
				{
					xmlpp::Node* currentNode = dynamic_cast<const xmlpp::Node*>(*iter);
					if(currentNode->get_name() == "trk")
					///@brief current Node is the track
					{
						xmlpp::Node::NodeList listTrackSeg = currentNode->get_children();
						for(xmlpp::Node::NodeList::iterator jiter = listTrackSeg.begin(); jiter != listTrackSeg.end(); ++jiter)
						{
							xmlpp::Node* isTrackSeg = dynamic_cast<const xmlpp::Node*>(*jiter);
							if(isTrackSeg->get_name() == "trkseg")
							{
								TrackSegment newTrack;
								if(newTrack.ReadXMLNode(isTrackSeg))
									segments.push_back(newTrack);
							}
						}
					}
				}
			}
		}
	}
	catch(const std::exception& ex)
	{
		std::cerr << "Exception caught: " << ex.what() << std::endl;
	}
	
	return true;
}

///@brief Write track to a Gpx file
///@param path Path to the GPX file to read
///@return true if succes
bool Track::WriteGPX(std::string path)
{
	std::ofstream fout(path.c_str());
	fout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
	fout << "<gpx version=\"v1.1.5\" creator=\"TraningBook\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/1\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.topografix.com/GPX/gpx_overlay/0/3 http://www.topografix.com/GPX/gpx_overlay/0/3/gpx_overlay.xsd http://www.topografix.com/GPX/gpx_modified/0/1 http://www.topografix.com/GPX/gpx_modified/0/1/gpx_modified.xsd\">\n";
	fout << "\t<trk>\n";
	fout << "\t\t<name>TrainingBook" << GetStartDate().AsString("yyyy/mm/dd_hh:nn:ss") << "</name>\n";
	WriteXML(fout);
	fout << "\t</trk>\n";
	fout << "</gpx>\n";

	return true;
}

///@brief Return length of the track
///@return Track length
double Track::Length()
{
	double res = 0.;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		res += (*lit).Length();
	}
	return res;
}

///@brief Return total duration
///@return Trip duration
Duration Track::GetDuration()
{
	Duration res;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		res += (*lit).GetDuration();
	}
	return res;
}

///@brief Return the start date of the GPX file
///@return Track start date
DateTime Track::GetStartDate()
{
	DateTime res;
	std::list<TrackSegment>::iterator lstart(segments.begin());
	res = (*lstart).GetStartDate();
	return res;
}

///@brief Return total ascent
///@return Total ascent
double Track::TotalAscent()
{
	double res = 0.;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		res += (*lit).TotalAscent();
	}
	return res;
}

///@brief Return total descent
///@return Total descent
double Track::TotalDescent()
{
	double res = 0.;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		res += (*lit).TotalDescent();
	}
	return res;	
}

///@brief Clear the current track
void Track::Clear()
{
	segments.clear();
}

///@brief Return a list containing the distance value of each point of the track
///@return list of double containing the distance evolution
std::list<double> Track::GetDistanceList()
{
	std::list<double> res;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		(*lit).AppendToDistanceList(res);
	}
	return res;	
}

///@brief Return a list containing the elevation value of each point of the track
///@return list of double containing the elevation evolution
std::list<double> Track::GetElevationList()
{
	std::list<double> res;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		(*lit).AppendToElevationList(res);
	}
	return res;	
}

///@brief Return a list containing the speed value of each point of the track
///@return list of double containing the speed evolution
std::list<double> Track::GetSpeedList()
{
	std::list<double> res;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		(*lit).AppendToSpeedList(res);
	}
	return res;
}

///@brief Return a list containing the latitude value of each point of the track
///@return list of double containing the latitude evolution
std::list<double> Track::GetLatList()
{
	std::list<double> res;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		(*lit).AppendToLatList(res);
	}
	return res;
}

///@brief Return a list containing the longitude value of each point of the track
///@return list of double containing the longitude evolution
std::list<double> Track::GetLonList()
{
	std::list<double> res;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		(*lit).AppendToLonList(res);
	}
	return res;
}

///@brief Return minimum value of elevation
///@return minimum elevation value
double Track::MinElevation()  
{
	double res = 100000.;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MinElevation();
		if(val < res)
			res = val;
	}
	return res;	
}

///@brief Return maximum value of elevation
///@return maximum elevation value
double Track::MaxElevation()
{
	double res = -10000.;
	std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MaxElevation();
		if(val > res)
			res = val;
	}
	return res;	
}

///@brief Return maximum value of speed
///@return maximum speed
double Track::MaxSpeed()
{
	double res = 0.;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MaxSpeed();
		if(val > res)
			res = val;
	}
	return res;	
}

///@brief Return maximum value of latitude
///@return maximum value of latitude
double Track::MaxLat()
{
	double res = -1000.;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MaxLat();
		if(val > res)
			res = val;
	}
	return res;
}

///@brief Return minimum value of latitude
///@return minimum value of latitude
double Track::MinLat()
{
	double res = 1000.;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MinLat();
		if(val < res)
			res = val;
	}
	return res;
}

///@brief Return maximum value of longitude
///@return maximum value of longitude
double Track::MaxLon()
{
	double res = -1000.;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MaxLon();
		if(val > res);
			res = val;
	}
	return res;
}

///@brief Return minimum value of longitude
///@return mimum value of longitude
double Track::MinLon()
{
	double res = 1000.;
	std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
	for(;lit != lend ; ++lit)
	{
		double val = (*lit).MinLon();
		if(val < res)
			res = val;
	}
	return res;
}

///@brief Write to XML file
///@param fout out stream to XML file
void Track::WriteXML(std::ofstream &fout)
{
    std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
    for(;lit != lend ; ++lit)
    {
        TrackSegment seg = (*lit);
        fout << "\t\t\t\t<trkseg>\n";
        seg.WriteXML(fout);
        fout << "\t\t\t\t</trkseg>\n";
    }
}

///@brief Add a new segment to the track
///@param seg TrackSegment to add
void Track::AddSegment(TrackSegment seg)
{
    segments.push_back(seg);
}

///@brief Test if the track is empty
///@return true if the track is empty
bool Track::Empty()
{
    return segments.empty();
}

///@brief Filter the track by keeping points separated by a given distance, return the number of suppressed points
///@param dist Minimum distance between the kept points in meters
///@return number of suppressed points
unsigned int Track::FilterByDistance(double dist)
{
    unsigned int res = 0;
    std::list<TrackSegment>::iterator lit (segments.begin()), lend(segments.end());
    for(;lit != lend ; ++lit)
    {
        res += (*lit).FilterByDistance(dist);
    }
    return res;
}

///@brief Filter the track by keeping points separated by a given elevation variation, return the number of suppressed points
///@param altvar Minimum elevation variation between the kept points in meters
///@return number of suppressed points
unsigned int Track::FilterByElevationVariation(double altvar)
{
    unsigned int res = 0;
    std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
    for(; lit != lend ; ++lit)
    {
        res += (*lit).FilterByElevationVariation(altvar);
    }
    return res;
}

///@brief Smooth the elevation values with given buffer size
///@param bufsize Buffer size for the sliding mean
void Track::SmoothElevation(unsigned int bufsize)
{
    std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
    for(; lit != lend ; ++lit)
    {
        (*lit).SmoothElevation(bufsize);
    }
}

///@brief Recompute the speed values
void Track::RecomputeSpeed()
{
    std::list<TrackSegment>::iterator lit(segments.begin()), lend(segments.end());
    for(; lit != lend ; ++lit)
    {
        (*lit).RecomputeSpeed();
    }
}

///@brief Return a pointer to the first segments
std::list<TrackSegment>::iterator Track::GetFirstSegment()
{
    return segments.begin();
}

///@brief Write a part of the content of the track to the standard output
void Track::WriteForDebug()
{
    for(std::list<TrackSegment>::iterator lit(segments.begin());
        lit != segments.end();
        ++lit)
        (*lit).WriteForDebug();
}